namespace GomuLibrary.IO.DiscImage
{
    using System;
    using System.IO;
    using System.Runtime.CompilerServices;

    public class Iso9660Conv
    {
        private BinaryReader _brSrc;
        private IDiscImage _discImage;
        private FileStream _fsDst;
        private FileStream _fsSrc;
        private SectorStructure _sectorStruct;
        private long _startFirstSector;
        private BinaryWriter _wrDst;
        private const int BUFFER_LENGTH_FACTOR = 0x40;
        private const uint FAT32_DISK_LIMIT = uint.MaxValue;
        private const int ISO_SECTOR_SIZE = 0x800;
        internal static byte[] SYNC_HEADER = new byte[] { 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0 };

        public event EventHandler<Iso9660CreatorEventArgs> Progression;

        public event EventHandler Terminate;

        public Iso9660Conv()
        {
            this._fsSrc = null;
            this._fsDst = null;
            this._brSrc = null;
            this._wrDst = null;
        }

        public Iso9660Conv(IDiscImage converter)
        {
            this._fsSrc = null;
            this._fsDst = null;
            this._brSrc = null;
            this._wrDst = null;
            this._discImage = converter;
            this.SetDiscImage();
        }

        private byte[] CleanUserDataBuffer(byte[] source, uint dataSize)
        {
            byte[] dst = new byte[dataSize];
            int num = 0;
            for (int i = 0; i < dataSize; i += 0x800)
            {
                if ((dataSize - i) >= 0x800L)
                {
                    Buffer.BlockCopy(source, (num * this._sectorStruct.sectorLength) + this._sectorStruct.userDataOffset, dst, num * 0x800, 0x800);
                    num++;
                }
                else
                {
                    Buffer.BlockCopy(source, (this._sectorStruct.sectorLength * num) + this._sectorStruct.userDataOffset, dst, num * 0x800, ((int) dataSize) - i);
                }
            }
            return dst;
        }

        public void Close()
        {
            if (this._brSrc != null)
            {
                this._brSrc.Close();
            }
            if (this._wrDst != null)
            {
                this._wrDst.Close();
            }
            if (this._fsSrc != null)
            {
                this._fsSrc.Close();
                this._fsSrc.Dispose();
            }
            if (this._fsDst != null)
            {
                this._fsDst.Close();
                this._fsDst.Dispose();
            }
        }

        public void Convert(string isoDestination)
        {
            if (this._discImage == null)
            {
                throw new ArgumentNullException("The IDiscImage interface is null. You can specified an instance from DiscImage propertie.");
            }
            if (isoDestination == string.Empty)
            {
                throw new ArgumentNullException("output", "The output file path must not be null.");
            }
            if (this._sectorStruct.sectorLength == 0)
            {
                throw new IOException("The source image file format is unknown.");
            }
            if (!File.Exists(this._discImage.ImagePath))
            {
                throw new FileNotFoundException("The souce image file does not exists.");
            }
            DriveInfo info = new DriveInfo(Path.GetPathRoot(isoDestination));
            FileInfo info2 = new FileInfo(this._discImage.ImagePath);
            long num = ((info2.Length / ((long) this._sectorStruct.sectorLength)) * 0x800L) - this._startFirstSector;
            if (info.DriveFormat.Equals("FAT32") && (num > 0xffffffffL))
            {
                throw new IOException("Not enough space on FAT32 destination drive.");
            }
            if (info.TotalSize < num)
            {
                throw new IOException("Not enough space on the destination drive.");
            }
            try
            {
                this._fsSrc = new FileStream(this._discImage.ImagePath, FileMode.Open, FileAccess.Read, FileShare.Read);
                this._brSrc = new BinaryReader(this._fsSrc);
                this._fsDst = new FileStream(isoDestination, FileMode.Create, FileAccess.Write, FileShare.None);
                this._wrDst = new BinaryWriter(this._fsDst);
                this._brSrc.BaseStream.Seek(this._startFirstSector, SeekOrigin.Begin);
                byte[] buffer = new byte[this._sectorStruct.sectorLength * 0x40];
                int num2 = 0;
                do
                {
                    num2 = this._brSrc.Read(buffer, 0, buffer.Length);
                    if (num2 == (this._sectorStruct.sectorLength * 0x40))
                    {
                        this._wrDst.Write(this.CleanUserDataBuffer(buffer, 0x20000));
                    }
                    else if (num2 > 0)
                    {
                        uint dataSize = System.Convert.ToUInt32((int) ((num2 / this._sectorStruct.sectorLength) * 0x800));
                        this._wrDst.Write(this.CleanUserDataBuffer(buffer, dataSize));
                    }
                    if (this.Progression != null)
                    {
                        this.Progression(null, new Iso9660CreatorEventArgs(this._wrDst.BaseStream.Position, this._brSrc.BaseStream.Length));
                    }
                }
                while ((num2 > 0) && (this._wrDst.BaseStream.Position != num));
                if (this.Terminate != null)
                {
                    this.Terminate(null, EventArgs.Empty);
                }
            }
            catch (Exception exception)
            {
                throw new Exception(exception.Message, exception);
            }
            finally
            {
                this.Close();
            }
        }

        public void Convert(IDiscImage discImage, string isoDestination)
        {
            if (discImage == null)
            {
                throw new ArgumentNullException("discImage", "The IDiscImage interface is null.");
            }
            this._discImage = discImage;
            this.SetDiscImage();
            this.Convert(isoDestination);
        }

        private void SetDiscImage()
        {
            this._sectorStruct = this._discImage.GetSectorStucture();
            this._startFirstSector = this._discImage.FirstDumpSector();
        }

        public IDiscImage DiscImage
        {
            get => 
                this._discImage;
            set
            {
                this._discImage = value;
                this.SetDiscImage();
            }
        }
    }
}

